﻿// // ******************************************************************
// //       /\ /|       @file       MediaCommandHandler.cs
// //       \ V/        @brief      await29
// //       | "")       @author     Await, 285283010@qq.com
// //       /  |
// //      /  \\        @Modified   2025-03-02 18:03
// //    *(__\_\        @Copyright  Copyright (c) , 2025-01-04 17:01 Await
// // ******************************************************************

using AutomaticSubtitles.Service.Application.LanguageConversion.Queries;
using AutomaticSubtitles.Service.Application.Media.Commands;
using AutomaticSubtitles.Service.Application.Media.Queries;
using AutomaticSubtitles.Service.DataAccess.Model;
using AutomaticSubtitles.Service.Domain.Media;
using AutomaticSubtitles.Service.Infrastructure.Helper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Data.Sqlite;
using System.IO;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using AutomaticSubtitles.Service.Domain.Config;

namespace AutomaticSubtitles.Service.Application.Media;

public class MediaCommandHandler(
    IServiceProvider serviceProvider,
    IMediaRepository repository,
    IEventBus eventBus,
    ICacheKeyRepository cacheKeyRepository,
    ISubtitleRepository subtitle,
    ILogger<MediaCommandHandler> logger) : BaseAutoService
{
    [EventHandler]
    public async Task CreateMedia(AddMediaListDataCommand command)
    {
        using var scope = serviceProvider.CreateScope();
        var scopedRepository = scope.ServiceProvider.GetRequiredService<IMediaRepository>();
        var scopedLogger = scope.ServiceProvider.GetRequiredService<ILogger<MediaCommandHandler>>();
        //获取现有数据 数据根据path + Leve 去重，删除不存在的数据，command.mediae 是所有最新数据,存在的数据更新UpdateTime时间
        var existingMedias = (await scopedRepository.GetListAsync()).ToList();
        if (command.mediae != null)
        {
            var existingPaths = existingMedias.ToDictionary(m => m.Path + m.Leve);
            var newMedias = command.mediae.Where(m => !existingPaths.ContainsKey(m.Path + m.Leve)).ToList();

            // 检查每个新媒体的ID，确保不会有重复
            var usedIds = new HashSet<string>(existingMedias.Select(m => m.Id));
            foreach (var media in newMedias)
                // 检查ID是否已经存在
                if (usedIds.Contains(media.Id))
                {
                    scopedLogger.LogWarning($"检测到媒体ID冲突，重新生成ID: {media.Id}");
                    // 生成新的ID
                    var newId = BasicsHelper.GetId();
                    // 确保新ID不会冲突
                    while (usedIds.Contains(newId))
                    {
                        scopedLogger.LogWarning($"生成的新ID仍然存在冲突，再次尝试: {newId}");
                        newId = BasicsHelper.GetId();
                    }

                    // 设置新ID并添加到已使用ID集合
                    media.SetId(newId);
                    usedIds.Add(newId);
                }
                else
                {
                    usedIds.Add(media.Id);
                }

            // 处理需要删除的媒体
            var deleteMedias = new List<Auto_Media>();

            // 基于路径列表删除
            if (command.RemovedPaths != null && command.RemovedPaths.Any())
            {
                // 查找被删除的媒体文件对应的媒体实体
                var removedPathMedias = existingMedias.Where(em =>
                    command.RemovedPaths.Any(path =>
                            em.Path == path ||
                            em.Path.StartsWith(path + Path.DirectorySeparatorChar) || // 目录内的文件
                            path.StartsWith(em.Path + Path.DirectorySeparatorChar)    // 文件所在目录
                    )
                ).ToList();

                deleteMedias.AddRange(removedPathMedias);
                scopedLogger.LogInformation($"--根据路径删除媒体 {removedPathMedias.Count} 条");
            }

            // 基于对比删除（原有实现）
            var comparisonDeleteMedias = existingMedias.Where(em =>
                    !command.mediae.Any(m => m.Path == em.Path && m.Leve == em.Leve) &&
                    !deleteMedias.Any(dm => dm.Id == em.Id) // 避免重复添加
            ).ToList();

            deleteMedias.AddRange(comparisonDeleteMedias);

            // 添加新媒体
            try
            {
                await scopedRepository.AddRangeAsync(newMedias);
            }
            catch (DbUpdateException ex) when (ex.InnerException is SqliteException sqlEx &&
                                               (sqlEx.Message.Contains("UNIQUE constraint failed") || sqlEx.SqliteErrorCode == 19))
            {
                scopedLogger.LogError(ex, "批量添加媒体时发生唯一约束错误，尝试逐个添加以隔离问题");

                // 如果批量添加失败，尝试逐个添加以找出问题记录
                foreach (var media in newMedias)
                    try
                    {
                        // 再次检查ID
                        var existingMedia = await scopedRepository.FindAsync(x => x.Id == media.Id);
                        if (existingMedia != null)
                        {
                            // ID冲突，生成新ID
                            var newId = $"media_{DateTime.Now.Ticks}_{Path.GetRandomFileName().Replace(".", "")}";
                            scopedLogger.LogWarning($"检测到媒体ID冲突，为媒体 {media.Name} 生成新ID: {newId}，原ID: {media.Id}");
                            media.SetId(newId);

                            // 再次检查新生成的ID
                            existingMedia = await scopedRepository.FindAsync(x => x.Id == newId);
                            if (existingMedia != null)
                            {
                                // 极少数情况下，新生成的ID仍然冲突
                                newId = $"media_{DateTime.Now.Ticks}_{Guid.NewGuid():N}_{Path.GetRandomFileName().Replace(".", "")}";
                                scopedLogger.LogWarning($"新生成的ID仍然冲突，使用更复杂的ID: {newId}");
                                media.SetId(newId);
                            }
                        }

                        await scopedRepository.AddAsync(media);
                        scopedLogger.LogInformation($"成功添加媒体: {media.Name}, ID: {media.Id}");
                    }
                    catch (DbUpdateException itemEx) when (itemEx.InnerException is SqliteException itemSqlEx &&
                                                           (itemSqlEx.Message.Contains("UNIQUE constraint failed") || itemSqlEx.SqliteErrorCode == 19))
                    {
                        // 详细记录具体是哪个约束失败
                        scopedLogger.LogError(itemEx, $"添加媒体 {media.Name} 时发生唯一约束错误: {itemEx.InnerException.Message}");

                        // 尝试最后一次使用完全随机的ID
                        try
                        {
                            var finalId = $"media_final_{DateTime.Now.Ticks}_{Guid.NewGuid():N}_{Random.Shared.Next(10000, 99999)}";
                            media.SetId(finalId);
                            await scopedRepository.AddAsync(media);
                            scopedLogger.LogWarning($"使用最终备选ID {finalId} 成功添加媒体: {media.Name}");
                        }
                        catch (Exception finalEx)
                        {
                            scopedLogger.LogError(finalEx, $"使用最终备选ID添加媒体 {media.Name} 仍然失败，跳过此记录");
                            // 继续处理下一个，不中断整个批次
                        }
                    }
                    catch (Exception itemEx)
                    {
                        scopedLogger.LogError(itemEx, $"添加单个媒体时出错: {media.Name}, Path: {media.Path}, 跳过此记录");
                        // 继续处理下一个，不中断整个批次
                    }
            }

            await scopedRepository.RemoveRangeAsync(deleteMedias);
            var updateMedias = existingMedias.Where(em => command.mediae.Any(m => m.Path == em.Path && m.Leve == em.Leve)).ToList();
            foreach (var item in updateMedias)
            {
                item.UpdateTime = DateTime.Now;
                // 单独更新每个实体，避免跟踪冲突
                await scopedRepository.UpdateAsync(item);
            }

            // 确保提交工作单元
            if (scopedRepository.UnitOfWork != null)
            {
                await scopedRepository.UnitOfWork.SaveChangesAsync();
                await scopedRepository.UnitOfWork.CommitAsync();
            }

            // 获取数据库中所有媒体信息，而不仅仅是新添加的
            var allMedias = (await scopedRepository.GetListAsync()).ToList();

            //统计
            command.Result = allMedias; // 修改这里，返回所有媒体信息
            command.DeleteMedia = deleteMedias;
            //日志
            var msg = $"新增媒体 {newMedias.Count} 条；删除媒体 {deleteMedias.Count} 条；更新媒体 {updateMedias.Count} 条；总媒体数 {allMedias.Count} 条";
            scopedLogger.LogWarning("{Message}", msg);
        }
        else
        {
            //删除所有数据
            await scopedRepository.RemoveRangeAsync(existingMedias);
            var msg = $"删除媒体 {existingMedias.Count} 条；";
            scopedLogger.LogWarning(msg);
            command.Result = null;
            command.DeleteMedia = existingMedias;

            // 确保提交工作单元
            if (scopedRepository.UnitOfWork != null)
            {
                await scopedRepository.UnitOfWork.SaveChangesAsync();
                await scopedRepository.UnitOfWork.CommitAsync();
            }
        }
    }

    [EventHandler]
    public async Task Handle(ObtainTranslationMediaQuery command)
    {
        //TODO:语言对应转换
        var media = await repository.GetListAsync(x => x.MediaType == GlobalVariableEnumConfiguration.MediaEnum.电影);
        var mediaTv = await repository.GetListAsync(x => x.MediaType == GlobalVariableEnumConfiguration.MediaEnum.电视剧);
        //排除路径
        var noData = await cacheKeyRepository.GetListAsync(x => x.Key == CacheCommon.CacheNoKey && x.IsShow == true);
        var excludePaths = noData.SelectMany(x => x.Value.Split('\n')).ToList();
        command.MVResult = await GetMedea(command.data, media, excludePaths);
        command.Result = await GetMedea(command.data, mediaTv, excludePaths);
    }

    [EventHandler]
    public async Task Handle(ObtainTranslationMediaToDataQuery command)
    {
        var media = command.Mediae.Where(x => x.MediaType == GlobalVariableEnumConfiguration.MediaEnum.电影);
        var mediaTv = command.Mediae.Where(x => x.MediaType == GlobalVariableEnumConfiguration.MediaEnum.电视剧);
        //排除路径
        var noData = await cacheKeyRepository.GetListAsync(x => x.Key == CacheCommon.CacheNoKey && x.IsShow == true);
        var excludePaths = noData.SelectMany(x => x.Value.Split('\n')).ToList();
        command.MVResult = await GetMedea(command.data, media, excludePaths);
        command.Result = await GetMedea(command.data, mediaTv, excludePaths);
    }

    private async Task<Dictionary<string, List<Auto_Media>>> GetMedea(Dictionary<string, bool> command, IEnumerable<Auto_Media> mediaTv, List<string> excludePaths)
    {
        var getLanguageConversionQuery = new GetLanguageConversionQuery(command.Select(x => x.Key).ToList());
        await eventBus.PublishAsync(getLanguageConversionQuery);
        var data = new Dictionary<string, List<Auto_Media>>();
        foreach (var autoMedia in mediaTv)
        {
            if (excludePaths.Any(x => autoMedia.Path.StartsWith(x))) continue;
            var autoSubtitles = (await subtitle.GetListAsync(x => x.VideoId == autoMedia.Id)).ToList();
            foreach (var keyValuePair in getLanguageConversionQuery.Result)
            {
                var isTip = command.TryGetValue(keyValuePair.Key, out var isTipValue) && isTipValue;
                if (autoSubtitles.Count == 0 || isTip)
                {
                    if (data.TryGetValue(keyValuePair.Key, out var list2))
                        list2.Add(autoMedia);
                    else
                        data.Add(keyValuePair.Key, [autoMedia]);
                    continue;
                }

                var count = autoSubtitles.Where(x => x.Language != null).Count(x => keyValuePair.Value.Contains(x.Language!));
                if (count > 0) continue;
                // var firstOrDefault = autoSubtitles.First();
                if (data.TryGetValue(keyValuePair.Key, out var list))
                    list.Add(autoMedia);
                else
                    data.Add(keyValuePair.Key, [autoMedia]);
            }
        }

        return data;
    }
}